﻿using System;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using CjcAwesomiumWrapper;
using Microsoft.Win32.SafeHandles;
using Microsoft.Surface.Presentation.Controls;
using Microsoft.Surface.Presentation;
using System.IO;

namespace Cjc.ChromiumBrowser
{
    /// <summary>
    ///     <Cjc.ChromiumBrowser:WebBrowser/>
    /// </summary>
    [TemplatePart(Name = "PART_Browser", Type = typeof(Image))]
    public class WebBrowser : SurfaceContentControl, IDisposable
    {
        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
            "Source",
            typeof(string),
            typeof(WebBrowser),
            new PropertyMetadata(OnSourceChanged));

        public string Source
        {
            get { return (string)GetValue(SourceProperty); }
            set { SetValue(SourceProperty, value); }
        }

        public static readonly DependencyProperty RenderPriorityProperty = DependencyProperty.Register(
            "RenderPriority",
            typeof(DispatcherPriority),
            typeof(WebBrowser),
            new PropertyMetadata(DispatcherPriority.Render));

        public DispatcherPriority RenderPriority
        {
            get { return (DispatcherPriority)GetValue(RenderPriorityProperty); }
            set { SetValue(RenderPriorityProperty, value); }
        }

        public static readonly DependencyProperty IsTransparentProperty = DependencyProperty.Register(
            "IsTransparent",
            typeof(bool),
            typeof(WebBrowser),
            new PropertyMetadata(false, OnIsTransparentChanged));

        public bool IsTransparent
        {
            get { return (bool)GetValue(IsTransparentProperty); }
            set { SetValue(IsTransparentProperty, value); }
        }

        public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register(
            "IsActive",
            typeof(bool),
            typeof(WebBrowser),
            new PropertyMetadata(true));

        public bool IsActive
        {
            get { return (bool)GetValue(IsActiveProperty); }
            set { SetValue(IsActiveProperty, value); }
        }

        public static readonly DependencyProperty EnableAsyncRenderingProperty = DependencyProperty.Register(
            "EnableAsyncRendering",
            typeof(bool),
            typeof(WebBrowser),
            new PropertyMetadata(false));

        public bool EnableAsyncRendering
        {
            get { return (bool)GetValue(EnableAsyncRenderingProperty); }
            set { SetValue(EnableAsyncRenderingProperty, value); }
        }

        public static readonly DependencyProperty BitmapProperty = DependencyProperty.Register(
            "Bitmap",
            typeof(WriteableBitmap),
            typeof(WebBrowser),
            null);

        public WriteableBitmap Bitmap
        {
            get { return (WriteableBitmap)GetValue(BitmapProperty); }
            private set { SetValue(BitmapProperty, value); }
        }

        public class StatusEventArgs : EventArgs
        {
            public string Message { get; private set; }

            public StatusEventArgs(string message)
            {
                this.Message = message;
            }
        }

        public class UrlEventArgs : EventArgs
        {
            public string Url { get; private set; }
            public string FrameName { get; private set; }

            public UrlEventArgs(string url, string frameName)
            {
                this.Url = url;
                this.FrameName = frameName;
            }
        }

        public class LoadingEventArgs : UrlEventArgs
        {
            public int StatusCode { get; private set; }
            public string MimeType { get; private set; }

            public LoadingEventArgs(string url, string frameName, int statusCode, string mimeType)
                : base(url, frameName)
            {
                this.StatusCode = statusCode;
                this.MimeType = mimeType;
            }
        }

        public class ContentEventArgs : EventArgs
        {
            public string Content { get; private set; }
            public string FrameName { get; private set; }

            public ContentEventArgs(string content, string frameName)
            {
                this.Content = content;
                this.FrameName = frameName;
            }
        }

        public class CallbackEventArgs : EventArgs
        {
            public string ObjectName { get; private set; }
            public string CallbackName { get; private set; }
            public object[] Arguments { get; private set; }

            public CallbackEventArgs(string objectName, string callbackName, object[] arguments)
            {
                this.ObjectName = objectName;
                this.CallbackName = callbackName;
                this.Arguments = arguments;
            }
        }

        public class ContentChangedEventArgs : EventArgs
        {
            public Int32Rect Rect { get; private set; }

            public ContentChangedEventArgs(Int32Rect rect)
            {
                this.Rect = rect;
            }
        }

        public event EventHandler Ready;
        public event EventHandler<StatusEventArgs> Status;
        public event EventHandler<UrlEventArgs> BeginNavigation;
        public event EventHandler<LoadingEventArgs> BeginLoading;
        public event EventHandler FinishLoading;
        public event EventHandler<ContentEventArgs> ReceiveTitle;
        public event EventHandler<CallbackEventArgs> Callback;
        public event EventHandler<ContentChangedEventArgs> ContentChanged;

        private static WebCore webCore;

        private bool disposed;
        private WebView webView;
        private WebViewListener webViewListener;

        public WebViewListener WebViewListener
        {
            get { return webViewListener; }
            set { webViewListener = value; }
        }

        private Image image;
        private ToolTip tooltip;
        private byte[] buffer, alterbuffer;
        private PixelFormat pixelFormat = PixelFormats.Bgr32;
        private Matrix deviceTransform;
 
        private double offset;

        public double Offset
        {

            set
            {
                offset = value;

            }
        }

        public bool forward = true;

        private bool isBrowserFocused;
        private string loadedUrl;

        /// <summary>
        /// Initializes the <see cref="WebBrowser"/> class.
        /// </summary>
        static WebBrowser()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(WebBrowser), new FrameworkPropertyMetadata(typeof(WebBrowser)));

            webCore = new WebCore();

            CompositionTarget.Rendering += delegate { webCore.Update(); };
        }



        public void injectMP(Point pos)
        {

            var x = pos.X * deviceTransform.M11;
            var y = pos.Y * deviceTransform.M22;

            webView.InjectMouseMove((int)x, (int)y);

        }

        public void injectMousePositionAndClick(Point pos)
        {

            var x = pos.X * deviceTransform.M11;
            var y = pos.Y * deviceTransform.M22;

            webView.InjectMouseMove((int)x, (int)y);

            if (webView != null)
            {
                webView.InjectMouseDown(GetMouseButton(System.Windows.Input.MouseButton.Left));
                webView.InjectMouseUp(GetMouseButton(System.Windows.Input.MouseButton.Left));
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WebBrowser"/> class.
        /// </summary>
        public WebBrowser()
        {
            KeyboardNavigation.SetAcceptsReturn(this, true);

            tooltip = new ToolTip
            {
                HasDropShadow = true,
                IsOpen = false,
                StaysOpen = true
            };

            CompositionTarget.Rendering += CompositionTarget_Rendering;
        }

        /// <summary>
        /// When overridden in a derived class, is invoked whenever application code or internal processes call <see cref="M:System.Windows.FrameworkElement.ApplyTemplate"/>.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            image = (Image)GetTemplateChild("PART_Image");

            if (image == null)
            {
                Content = image = new Image
                {
                    Focusable = false,

                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Top,
                    Stretch = Stretch.None
                };
            }
        }


        public void savePixs()
        {

            alterbuffer = new byte[4 * Bitmap.PixelWidth * Bitmap.PixelHeight];
            Bitmap.CopyPixels(new Int32Rect(0, 0, Bitmap.PixelWidth, Bitmap.PixelHeight), alterbuffer, Bitmap.BackBufferStride, 0);
        }

        public void CompositionTarget_ComboRendering()
        {
            if (imageHold > 1) { return; }

            int distribution = (int)offset;
            if (!forward && Bitmap.PixelWidth < distribution) { return; }

            if (Bitmap.PixelWidth < distribution) throw new Exception("out of bounds");

            var stride = Bitmap.BackBufferStride;
            webView.Render(buffer, stride, 4);

            var incoming = new Int32Rect(forward ? 0 : Bitmap.PixelWidth - distribution, 0, distribution, Bitmap.PixelHeight); // 

            var outcoming = new Int32Rect(forward ? distribution : 0, 0, Bitmap.PixelWidth - distribution, Bitmap.PixelHeight);

            if (outcoming.Width > 0 && outcoming.Height > 0)
            {

                Bitmap.WritePixels(
                    outcoming,
                    alterbuffer,
                    stride,
                    forward ? 0 : distribution,
                    0);
            }

            if (incoming.Width > 0 && incoming.Height > 0)
            {
                Bitmap.WritePixels(
                    incoming,
                    buffer,
                    stride,
                    forward ? Bitmap.PixelWidth - distribution : 0,
                    0);
            }

            if (image.Source != Bitmap)
            {
                Dispatcher.BeginInvoke(
                    (Action)delegate
                    {
                        image.Source = Bitmap;
                    },
                    (IsFocused) ? DispatcherPriority.Render : RenderPriority);
            }
        }

        /// <summary>
        /// Handles the CompositionTarget.Rendering event.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            if (imageHold > 0)
            {
                try
                {
                    CompositionTarget_ComboRendering();
                }
                catch(Exception ex)
                {
                    Console.Error.WriteLine(ex.Message);
                }
                return;
            }

            var bitmap = this.Bitmap;

            if (buffer != null && bitmap != null && webView != null && webView.IsDirty() && IsActive)
            {
                var stride = bitmap.BackBufferStride;

                var r = webView.Render(buffer, stride, 4);

                var rendered = EnableAsyncRendering
                    ? new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight)
                    : new Int32Rect(r.X, r.Y, r.Width, r.Height);

                if (rendered.Width > 0 && rendered.Height > 0)
                {
                    bitmap.WritePixels(
                        rendered,
                        buffer,
                        stride,
                        rendered.X,
                        rendered.Y);
                }

                if (image.Source != bitmap)
                {


                    Dispatcher.BeginInvoke(
                        (Action)delegate
                        {
                            image.Source = bitmap;
                            if (ContentChanged != null) ContentChanged(this, new ContentChangedEventArgs(rendered));
                        },
                        (IsFocused || IsMouseOver) ? DispatcherPriority.Render : RenderPriority);
                }

            }
        }

        /// <summary>
        /// Navigates the specified URL.
        /// </summary>
        /// <param name="url">The URL.</param>
        public void Navigate(string url)
        {
            if (webView != null) webView.LoadURL(url);
        }

        /// <summary>
        /// Loads HTML from a string.
        /// </summary>
        /// <param name="html">The HTML.</param>
        public void LoadHtml(string html)
        {
            if (webView != null) webView.LoadHTML(html);
        }

        /// <summary>
        /// Loads a local HTML file.
        /// </summary>
        /// <param name="path">The path.</param>
        public void LoadFile(string path)
        {
            if (webView != null) webView.LoadFile(path);
        }

        /// <summary>
        /// Goto the history offset.
        /// </summary>
        /// <param name="offset">The offset.</param>
        public void GotoHistoryOffset(int offset)
        {
            if (webView != null) webView.GotoHistoryOffset(offset);
        }

        /// <summary>
        /// Executes the javascript.
        /// </summary>
        /// <param name="javascript">The javascript.</param>
        /// <param name="frameName">Name of the frame.</param>
        public void ExecuteJavascript(string javascript, string frameName)
        {
            if (webView != null) webView.ExecuteJavascript(javascript, frameName);
        }

        /// <summary>
        /// Executes the javascript with result.
        /// </summary>
        /// <param name="javascript">The javascript.</param>
        /// <param name="frameName">Name of the frame.</param>
        /// <returns></returns>
        public JSValue ExecuteJavascriptWithResult(string javascript, string frameName)
        {
            return (webView != null)
                ? webView.ExecuteJavascriptWithResult(javascript, frameName)
                : null;
        }

        /// <summary>
        /// Creates an object.
        /// </summary>
        /// <param name="objectName">The object name.</param>
        public void CreateObject(string objectName)
        {
            if (webView != null) webView.CreateObject(objectName);
        }

        /// <summary>
        /// Destroys an object.
        /// </summary>
        /// <param name="objectName">The object name.</param>
        public void DestroyObject(string objectName)
        {
            if (webView != null) webView.DestroyObject(objectName);
        }

        /// <summary>
        /// Sets an object property.
        /// </summary>
        /// <param name="objectName">The object name.</param>
        /// <param name="propertyName">The property name.</param>
        /// <param name="value">The value.</param>
        public void SetObjectProperty(string objectName, string propertyName, JSValue value)
        {
            if (webView != null) webView.SetObjectProperty(objectName, propertyName, value);
        }

        /// <summary>
        /// Sets an object callback.
        /// </summary>
        /// <param name="objectName">The object name.</param>
        /// <param name="propertyName">The property name.</param>
        public void SetObjectCallback(string objectName, string propertyName)
        {
            if (webView != null) webView.SetObjectCallback(objectName, propertyName);
        }

        /// <summary>
        /// Gets the content as text.
        /// </summary>
        /// <param name="maxChars">The maximum number of characters.</param>
        /// <returns></returns>
        public string GetContentAsText(int maxChars)
        {
            return (webView != null) ? webView.GetContentAsText(maxChars) : null;
        }

        /// <summary>
        /// Sets whether to open external links in calling frame.
        /// </summary>
        /// <param name="isEnabled">if set to <c>true</c> [is enabled].</param>
        public void SetOpensExternalLinksInCallingFrame(bool isEnabled)
        {
            if (webView != null) webView.SetOpensExternalLinksInCallingFrame(isEnabled);
        }

        /// <summary>
        /// Called to arrange and size the content of a <see cref="T:System.Windows.Controls.Control"/> object.
        /// </summary>
        /// <param name="arrangeBounds">The computed size that is used to arrange the content.</param>
        /// <returns>The size of the control.</returns>
        protected override Size ArrangeOverride(Size arrangeBounds)
        {
            var size = base.ArrangeOverride(arrangeBounds);
            if (image != null)
            {

                //deviceTransform = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice;
                //deviceTransform = Matrix.Identity;


                var width = (int)(size.Width);
                var height = (int)(size.Height);


                if (width > 0 && height > 0)
                {
                    try
                    {
                        if (webView == null)
                        {
                            InitializeWebView(width, height);
                            AttachEventHandlers();

                            webView.LoadURL(Source ?? "about:blank");
                        }
                        else webView.Resize(width, height);

                        var bufferSize = width * height * 4;
                        if (buffer == null || buffer.Length != bufferSize) buffer = new byte[bufferSize];

                        var bitmap = this.Bitmap;

                        if ((bitmap == null || bitmap.PixelWidth != width || bitmap.PixelHeight != height))
                        {
                            this.Bitmap = new WriteableBitmap(
                                width, height,
                                96 * deviceTransform.M11, 96 * deviceTransform.M22,
                                pixelFormat, null);
                        }
                    }
                    catch { }
                }
            }

            return size;
        }

        /// <summary>
        /// Initializes the web view.
        /// </summary>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        private void InitializeWebView(int width, int height)
        {
            try
            {
                RaiseStatus("Creating WebView");

                webView = webCore.CreateWebView(width, height, IsTransparent, EnableAsyncRendering, 70);

                RaiseStatus("Initializign WebViewListener");

                webViewListener = new WebViewListener();

                webViewListener.BeginNavigation += delegate(string url, string frameName)
                {
                    RaiseStatus(string.Format("BeginNavigation: {0}", url));

                    if (string.IsNullOrEmpty(frameName) && url != loadedUrl)
                    {
                        loadedUrl = url;

                        Dispatcher.BeginInvoke((Action)delegate
                        {
                            Source = url;
                        },
                        DispatcherPriority.Render);
                    }

                    if (BeginNavigation != null)
                    {
                        Dispatcher.BeginInvoke(
                            (Action)delegate { BeginNavigation(this, new UrlEventArgs(url, frameName)); },
                            DispatcherPriority.Normal);
                    }
                };

                webViewListener.BeginLoading += delegate(string url, string frameName, int statusCode, string mimeType)
                {
                    RaiseStatus(string.Format("BeginLoading: {0}", url));

                    if (BeginLoading != null)
                    {
                        Dispatcher.BeginInvoke(
                            (Action)delegate { BeginLoading(this, new LoadingEventArgs(url, frameName, statusCode, mimeType)); },
                            DispatcherPriority.Normal);
                    }
                };

                webViewListener.FinishLoading += delegate
                {
                    RaiseStatus(string.Format("FinishLoading"));

                    if (FinishLoading != null)
                    {
                        Dispatcher.BeginInvoke(
                            (Action)delegate { FinishLoading(this, EventArgs.Empty); },
                            DispatcherPriority.Normal);
                    }
                };

                webViewListener.ReceiveTitle += delegate(string title, string frameName)
                {
                    RaiseStatus(string.Format("ReceiveTitle: {0}", title));

                    if (ReceiveTitle != null)
                    {
                        Dispatcher.BeginInvoke(
                            (Action)delegate { ReceiveTitle(this, new ContentEventArgs(title, frameName)); },
                            DispatcherPriority.Render);
                    }
                };

                webViewListener.ChangeCursor += delegate(ValueType cursorHandle)
                {
                    var safeHandle = new SafeFileHandle((IntPtr)cursorHandle, false);

                    Dispatcher.BeginInvoke((Action)delegate
                    {
                        Cursor = CursorInteropHelper.Create(safeHandle);
                    },
                    DispatcherPriority.Render);
                };

                webViewListener.ChangeTooltip += delegate(string text)
                {
                    Dispatcher.BeginInvoke((Action)delegate
                    {
                        try
                        {
                            if (text != null && text.Trim().Length > 0 && IsFocused)
                            {
                                tooltip.Content = text;
                                tooltip.IsOpen = true;
                            }
                            else tooltip.IsOpen = false;
                        }
                        catch
                        {
                        }
                    },
                    DispatcherPriority.Render);
                };

                webViewListener.ChangeKeyboardFocus += delegate(bool isFocused)
                {
                    isBrowserFocused = isFocused;
                };

                webViewListener.Callback += delegate(string objectName, string callbackName, JSValue[] args)
                {
                    var argValues = args.Select(a => a.Value()).ToArray();

                    RaiseStatus(string.Format("Callback"));

                    if (Callback != null)
                    {
                        Dispatcher.BeginInvoke(
                            (Action)delegate { Callback(this, new CallbackEventArgs(objectName, callbackName, argValues)); },
                            DispatcherPriority.Normal);
                    }
                };

                webView.SetListener(webViewListener);
                webView.Focus();

                if (Ready != null) Dispatcher.BeginInvoke((Action)delegate { Ready(this, EventArgs.Empty); }, DispatcherPriority.Normal);
            }
            catch (Exception ex)
            {
                RaiseStatus(ex.Message + ex.StackTrace);
            }
        }

        /// <summary>
        /// Attaches the event handlers.
        /// </summary>
        private void AttachEventHandlers()
        {
            RaiseStatus("Attaching event handlers");

            GotFocus += delegate
            {
                RaiseStatus("Got focus");
                if (webView != null) webView.Focus();
                isBrowserFocused = true;
            };

            LostFocus += delegate
            {
                RaiseStatus("Lost focus");
                if (webView != null) webView.Unfocus();
                tooltip.IsOpen = false;
            };

            KeyDown += delegate(object sender, KeyEventArgs e)
            {
                if (e.Key == Key.Tab && !isBrowserFocused)
                {
                    RaiseStatus("Allowed tab KeyDown for navigation");
                    return;
                }

                var k = e.ToKeyInfo();
                var isExtended = (k.ControlKeyState & ControlKeyStates.EnhancedKey) == ControlKeyStates.EnhancedKey;

                InjectKeyboardEvent(Win32Message.WM_KEYDOWN, (byte)k.VirtualKeyCode, isExtended, false, e.IsRepeat, false);

                if (k.Character != 0)
                {
                    var leftAlt = (k.ControlKeyState & ControlKeyStates.LeftAltPressed) == ControlKeyStates.LeftAltPressed;
                    var rightAlt = (k.ControlKeyState & ControlKeyStates.LeftAltPressed) == ControlKeyStates.RightAltPressed;

                    InjectKeyboardEvent(Win32Message.WM_CHAR, (byte)k.Character, isExtended, leftAlt | rightAlt, e.IsRepeat, false);
                }

                RaiseStatus("Handled KeyDown: " + e.Key);
                e.Handled = true;
            };

            KeyUp += delegate(object sender, KeyEventArgs e)
            {
                if (e.Key == Key.Tab && !isBrowserFocused)
                {
                    RaiseStatus("Allowed tab KeyUp for navigation");
                    return;
                }

                var k = e.ToKeyInfo();
                var isExtended = (k.ControlKeyState & ControlKeyStates.EnhancedKey) == ControlKeyStates.EnhancedKey;

                InjectKeyboardEvent(Win32Message.WM_KEYUP, (byte)k.VirtualKeyCode, isExtended, true, !e.IsRepeat, true);

                RaiseStatus("Handled KeyUp: " + e.Key);
                e.Handled = true;
            };

            MouseMove += delegate(object sender, MouseEventArgs e)
            {
                var pos = e.GetPosition(this);
                var x = pos.X * deviceTransform.M11;
                var y = pos.Y * deviceTransform.M22;

                webView.InjectMouseMove((int)x, (int)y);

                tooltip.PlacementTarget = this;
                tooltip.Placement = PlacementMode.Top;
                tooltip.HorizontalOffset = pos.X;
                tooltip.VerticalOffset = pos.Y;
            };

            MouseLeave += delegate
            {
                tooltip.IsOpen = false;
            };

            MouseDown += delegate(object sender, MouseButtonEventArgs e)
            {
                CaptureMouse();
                Keyboard.Focus(this);
                if (webView != null) webView.InjectMouseDown(GetMouseButton(e.ChangedButton));

                e.Handled = true;
            };

            MouseUp += delegate(object sender, MouseButtonEventArgs e)
            {
                if (webView != null) webView.InjectMouseUp(GetMouseButton(e.ChangedButton));
                ReleaseMouseCapture();

                e.Handled = true;
            };

            MouseWheel += delegate(object sender, MouseWheelEventArgs e)
            {
                if (webView != null) webView.InjectMouseWheel(e.Delta);
            };

        }

        /// <summary>
        /// Raises the status.
        /// </summary>
        /// <param name="message">The message.</param>
        private void RaiseStatus(string message)
        {
            if (Status != null) Status(this, new StatusEventArgs(message));
        }

        /// <summary>
        /// Injects the keyboard event.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="virtualKeyCode">The virtual key code.</param>
        /// <param name="isExtended">if set to <c>true</c> [is extended].</param>
        /// <param name="contextCode">if set to <c>true</c> [context code].</param>
        /// <param name="previousState">if set to <c>true</c> [previous state].</param>
        /// <param name="transitionState">if set to <c>true</c> [transition state].</param>
        private void InjectKeyboardEvent(Win32Message message, byte virtualKeyCode, bool isExtended, bool contextCode, bool previousState, bool transitionState)
        {
            if (webView != null)
            {
                var lParam = new KeyLParam(1, virtualKeyCode, isExtended, contextCode, previousState, transitionState);

                webView.InjectKeyboardEvent((IntPtr)0, (int)message, virtualKeyCode, lParam);
            }
        }

        /// <summary>
        /// Gets the mouse button.
        /// </summary>
        /// <param name="button">The button.</param>
        /// <returns></returns>
        private CjcAwesomiumWrapper.MouseButton GetMouseButton(System.Windows.Input.MouseButton button)
        {
            switch (button)
            {
                case System.Windows.Input.MouseButton.Middle: return CjcAwesomiumWrapper.MouseButton.Middle;
                case System.Windows.Input.MouseButton.Right: return CjcAwesomiumWrapper.MouseButton.Right;
                default: return CjcAwesomiumWrapper.MouseButton.Left;
            }
        }

        /// <summary>
        /// Releases the web view.
        /// </summary>
        private void ReleaseWebView()
        {
            CompositionTarget.Rendering -= CompositionTarget_Rendering;

            if (webView != null)
            {
                webView.Dispose();
                webView = null;
            }

            if (webViewListener != null)
            {
                webViewListener.Dispose();
                webViewListener = null;
            }
        }

        /// <summary>
        /// Called when [source changed].
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            if (args.NewValue != args.OldValue)
            {
                var webBrowser = obj as WebBrowser;
                var url = (string)args.NewValue;

                if (webBrowser != null && url != webBrowser.loadedUrl) webBrowser.Navigate(url);
            }
        }

        /// <summary>
        /// Called when [is transparent changed].
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnIsTransparentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var webBrowser = obj as WebBrowser;
            var isTransparent = (bool)args.NewValue;
            if (webBrowser.webView != null) webBrowser.webView.SetTransparent(isTransparent);
            webBrowser.pixelFormat = isTransparent ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
        }

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing) ReleaseWebView();

                disposed = true;
            }
        }

        #endregion

        public uint imageHold { get; set; }
    }
}